//------------------------------------------------------------------------------
// Author : Woo-Hyun Kim
// Date : Jun. 22, 2000
// Email : woorung@gmail.com
//
// [log]
// 1. deals with the problem for calling getFormData() but there is no parameter
//    rewritten by woorung (Apr. 24, 2002)
// 2. deals with the problem inclued the trash value at the end of some prameters
//	  rewritten by woorung (Aug. 5, 2002)
//------------------------------------------------------------------------------
#include "CCGI.h"

CCGI::CCGI()
{
	entryNum = 0;
	first = NULL;
	current = NULL;
	m_pszHanguelFileName = NULL;

	dataMaxSize = 1024;	
}

CCGI::CCGI(int size)
{
	entryNum = 0;
	first = NULL;
	current = NULL;
	m_pszHanguelFileName = NULL;

	dataMaxSize = size;
}

CCGI::~CCGI()
{
	if(m_pszHanguelFileName!=NULL) {
		delete []m_pszHanguelFileName;
		m_pszHanguelFileName = NULL;
	}

	entryNode * tmpNode = first;
	while(tmpNode) {
		current = tmpNode;
		tmpNode = tmpNode->link;
		delete current;
	}
}

bool CCGI::getFormData(void)
{
	const char * contentType = getContentType();

	if(contentType==NULL)
		return __getUrlEncodedData();
	else if(!strncmp(contentType,"multipart/form-data;",20))
		return __getMultiPartData();
	else
		return __getUrlEncodedData();
}

bool CCGI::__getMultiPartData(void)
{
	char boundary[256];			
	char last_boundary[256];	
	char strBuf[1024];	
	int intBuf;	

	sprintf(boundary,"--%s", strstr(getContentType(),"boundary=") + 9 );
	sprintf(last_boundary,"%s--",boundary);	

	fgets(strBuf, sizeof(strBuf), stdin);
	
	if(strncmp(strBuf,boundary,strlen(boundary))) {
		cerr << "Boundary error in parsing multipart data" << endl;
		return false;
	}

	bool endFlag = false;
	for(entryNum=1;1;entryNum++) {
		bzero((char*)strBuf,strlen(strBuf)); 

		char * tmpName = NULL;
		char * tmpFilename = NULL;
		long long int tmpFilesize = -1;
		char * tmpData = NULL;

		fgets(strBuf, sizeof(strBuf), stdin);

		int nmSize = 0; int i = 0;
		if( entryNum == 1 )
			i = strlen("Content-Disposition: form-data; name=\"");
		else i = strlen("Disposition: form-data; name=\"");

		while( strBuf[i+nmSize] != '\"' ) nmSize ++;
		tmpName = new char [nmSize+1];
		if( tmpName == 0 ) return false;

		strncpy(tmpName,&strBuf[i],nmSize);
		tmpName[nmSize] = '\0';

		bool fileFlag = false;
		if( strstr(strBuf,"filename=\"") != NULL ) {
			fileFlag = true;
			int fnSize = 0;

			while( strBuf[i+nmSize+13+fnSize] != '\"' ) fnSize++;
			tmpFilename = new char[fnSize+1];
			if( tmpFilename == 0 ) return false;
			strncpy(tmpFilename,&strBuf[i+nmSize+13],fnSize);

			__extractPath(tmpFilename);

			fgets(strBuf, sizeof(strBuf), stdin);
		}

		fgets(strBuf, sizeof(strBuf), stdin);

		tmpData = new char [1024*dataMaxSize];
		if( tmpData == 0 ) return false;

		long long int dtSize = 0;
		while( ( intBuf = fgetc(stdin) ) != EOF ) {
			tmpData[dtSize++] = static_cast<char>(intBuf);

			if( intBuf == '-' /*&& dtSize >= strlen(boundary)*/ )
				// in case of middle boundary
				if( !strncmp(boundary,&tmpData[dtSize-strlen(boundary)-strlen("content-")-2],strlen(boundary)))
                                {
					tmpData[dtSize-strlen(boundary)-strlen("content-")-4] = '\0';
					tmpFilesize = dtSize - strlen(boundary) - 12;
					break;
				}

                                // in case of last boundary
				else if( !strncmp(last_boundary,&tmpData[dtSize-strlen(last_boundary)],strlen(last_boundary)))
                                {
					tmpData[dtSize-strlen(last_boundary)-2] = '\0';
					tmpFilesize = dtSize - strlen(last_boundary) - 2;
					endFlag = true;
					break;
				}
		}

		entryNode * tmpNode = new entryNode [1];
		if( entryNum == 1 ) first = tmpNode;
		else current->link = tmpNode;

		current = tmpNode;

		current->entryName = tmpName;
		current->entryData = tmpData;
		if(tmpFilename) {
			//__plusToSpace(tmpFilename);
			//__escapeToChar(tmpFilename);
			current->entryFilename = tmpFilename;
		}
		current->entryFilesize = tmpFilesize;

		if( endFlag == true ) return true;
	}

	return false;
}

// in case of 'application/s-www-form-urlencoded' enctype
bool CCGI::__getUrlEncodedData(void)
{
	int contentLength = 0;
	char *dataString = NULL;
	register int i,j;
	const char * requestMethod = getRequestMethod();

	if(requestMethod==NULL) return false;

	// in case of 'get' request-method
	if(!strncmp(requestMethod,"GET",3)) {
		char *queryString = getQueryString();
		if(queryString==NULL||*queryString=='\0') {
			return false;
		}
		// to eliminate trash value (2002.08.05)
		dataString = new char[strlen(queryString)+1];
		if(dataString==0) return false;
		strcpy(dataString,queryString);
	}
	// in case of 'post' request-method
	else {
		contentLength = getContentLength();
		dataString = new char[contentLength+1];
		if(dataString==0) return false;

		for(i=0;i<contentLength;i++)
			*(dataString+i) = getc(stdin);
		*(dataString+contentLength) = '\0';
	}

	i = 0;
	while(*dataString!='\0') {
		if(!__getEntryName(dataString,i)) break;
		if(!__getEntryData(dataString,i++)) break;
		entryNum = i;
	}
	entryNum = i;

	return true;
}

// extract entry name
bool CCGI::__getEntryName(char * & dataString,int i)
{
	int j = 0;
	int stringSize = 0;
	char * tmpName = NULL;

	while( *dataString )
		if( *dataString=='='||*dataString=='\0') break;
		else { dataString++; stringSize++; }

	dataString -= stringSize;

	tmpName = new char [stringSize+1];
	tmpName[0] = '\0';
	while( *dataString!='='&&*dataString!='\0') *(tmpName+j++) = *dataString++;

	*(tmpName+j) = '\0';
	__plusToSpace(tmpName);
	__escapeToChar(tmpName);

	// insert to linked list
	entryNode * tmpNode = new entryNode[1];
	tmpNode->entryName = NULL;
	tmpNode->entryData = NULL;
	tmpNode->link = NULL;
	tmpNode->entryFilesize = -1;

	if( !i ) first = tmpNode;
	else current->link = tmpNode;

	current = tmpNode;
	current->entryName = tmpName;

	if(*dataString=='\0') return false;
	*dataString++;
	return true;
}

// extract entry data
bool CCGI::__getEntryData(char * & dataString,int i)
{
	int j = 0;
	int stringSize = 0;
	char * tmpData = NULL;

	while(*dataString)
		if(*dataString=='&'||*dataString=='\0') break;
		else { dataString++; stringSize++; }

	if( !stringSize ) {
		// deal with internal error when there is no value for a parameter
		current->entryData = NULL;
		current->link = NULL;
		current->entryFilesize = -1;
		if(*dataString=='\0') return false;
		*dataString++;
		return true;
	}

	dataString -= stringSize;

	tmpData = new char[stringSize+1];

	while((*dataString != '&')&&(*dataString != '\0'))
		*(tmpData+j++) = *dataString++;

	*(tmpData+j) = '\0';
	__plusToSpace(tmpData);
	__escapeToChar(tmpData);

	current->entryData = tmpData;
	current->entryFilesize = -1;

	if(*dataString == '\0') return false;
	*dataString++;
	return true;
}

// convert '+' to ' ' 
void CCGI::__plusToSpace(char * string)
{
	assert( *string );
	while( *string ) {
		if( *string == '+' ) *string = ' ';
		string++;
	}
}

// to urlencode written by woorung
bool CCGI::ACCEPTABLE(unsigned char a) 
{
	unsigned char isAcceptable[96] =  {
                /*   0 1 2 3 4 5 6 7 8 9 A B C D E F */
                     0,0,0,0,0,0,0,0,0,0,7,6,0,7,7,4,   /* 2x   !"#$%&'()*+,-./  */
                     7,7,7,7,7,7,7,7,7,7,0,0,0,0,0,0,   /* 3x  0123456789:;<=>?  */
                     7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,   /* 4x  @ABCDEFGHIJKLMNO  */
                     7,7,7,7,7,7,7,7,7,7,7,0,0,0,0,7,   /* 5X  PQRSTUVWXYZ[\]^_  */
                     0,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,   /* 6x  `abcdefghijklmno  */
                     7,7,7,7,7,7,7,7,7,7,7,0,0,0,0,0    /* 7X  pqrstuvwxyz{\}~DEL*/
	};
	unsigned char mask = (unsigned char) 1; 	

	if(a>=32&&a<128&&((isAcceptable[a-32])&mask)) return true;
	else return false;
}

char *CCGI::MakePostString(char *str)
{
	char _hex[] = "0123456789ABCDEF";
   	char * p;
   	char * q;
   	char * result;
   	int unacceptable = 0;

   	for(p=str;*p;p++) 
	{
		if(!ACCEPTABLE((unsigned char) *p)) unacceptable++;
	}

   	result = new char[p-str+unacceptable+unacceptable+1];
   	if (result==NULL) return NULL;

   	for (q=result,p=str;*p;p++) 
	{
       	unsigned char a = *p;
   		if (!ACCEPTABLE(a)) 
		{
       		*q++ = '%';
       		*q++ = _hex[a>>4];
       		*q++ = _hex[a&15];
   		}
   		else *q++ = *p;
   	}

   	*q++ = '\0';
   	return result;
}

// to deal with hanguel file name written by woorung
char *CCGI::toHanguelFileName(char * str)
{
	if(m_pszHanguelFileName!=NULL) {
		delete []m_pszHanguelFileName;
		m_pszHanguelFileName = NULL;	
	}

	m_pszHanguelFileName = MakePostString(str);

	return m_pszHanguelFileName;
}

// convert '%XX' to charactor
void CCGI::__escapeToChar(char * & string)
{
	register int i,j;
	register char digit;

	for(i=0,j=0;*(string+j);i++,j++) {
		if( (*(string+i) = *(string+j)) == '%' ) {
			digit = 16 * (*(string+j+1) >= 'A' ?
				toupper(*(string+j+1)) - 'A' + 10 : *(string+j+1) - '0');
			digit += *(string+j+2) >= 'A' ?
				toupper(*(string+j+2)) - 'A' + 10 : *(string+j+2) - '0';
			*(string+i) = digit;
			j+=2;
		}
	}
	*(string+i) = '\0';
}

// extract only file name from file name with path
void CCGI::__extractPath(char * & fname)
{
	int i = 0;
	int j = 0;
	for(;*(fname+i);i++)
		if( *(fname+i) == '\\' || *(fname+i) == '/' )
			j = i+1;
	strcpy(fname,&fname[j]);
}

// get 'CONTENT_TYPE' env. variable
char * CCGI::getContentType(void)
{
	return getenv("CONTENT_TYPE");
}

// get 'REQUEST_METHOD' env. variable
char * CCGI::getRequestMethod(void)
{
	return getenv("REQUEST_METHOD");
}

// get 'CONTENT_LENGTH' env. variable
int CCGI::getContentLength(void)
{
	return atoi(getenv("CONTENT_LENGTH"));
}

// get 'QUERY_STRING' env. variable
char * CCGI::getQueryString(void)
{
	return getenv("QUERY_STRING");
}

// get number of entry
int CCGI::getEntryNum(void)
{
	return entryNum;
}

// get entry name by index 
char * CCGI::getEntryName(const int i)
{
	if( i >= entryNum ) return NULL;
	entryNode * tmpNode = first;
	int s = 0;

	while(tmpNode!=NULL) {
                if(s==i) return tmpNode->entryName;
                tmpNode = tmpNode->link;
                s++;
    }
    return NULL;

/*
	while(1) {
		if(s==i) return tmpNode->entryName;
		tmpNode = tmpNode->link;
		s++;
	}
	return NULL;
*/
}

// get entry data by index 
char * CCGI::getEntryData(const int i)
{
	if( i >= entryNum ) return NULL;
	entryNode * tmpNode = first;
        int s = 0;

	while(tmpNode!=NULL) {
                if(s==i) return tmpNode->entryData;
                tmpNode = tmpNode->link;
                s++;
        }
        return NULL;

/*
    while(1) {
        if( s == i ) return tmpNode->entryData;
        tmpNode = tmpNode->link;
        s++;
    }
	return NULL;
*/
}

// get file name by index
char * CCGI::getEntryFilename(const int i)
{
	if( i >= entryNum ) return NULL;
	entryNode * tmpNode = first;
	int s = 0;

	while(tmpNode!=NULL) {
                if(s==i) return tmpNode->entryFilename;
                tmpNode = tmpNode->link;
                s++;
        }
        return NULL;

/*
	while(1) {
		if( s == i ) return tmpNode->entryFilename;
		tmpNode = tmpNode->link;
		s++;
	}
	return NULL;
*/
}

// get file size by index
long long int CCGI::getEntryFilesize(const int i)
{
	if( i >= entryNum ) return -1;
	entryNode * tmpNode = first;
	int s = 0;

	while(tmpNode!=NULL) {
		if(s==i) return tmpNode->entryFilesize;
                tmpNode = tmpNode->link;
		s++;
        }
        return -1;

/*
	while(1) {
		if( s == i ) return tmpNode->entryFilesize;
		tmpNode = tmpNode->link;
		s++;
	}
	return -1;
*/
}

// get entry data by entry name
char * CCGI::getEntryDataByEntryName(const char * name)
{
	entryNode * tmpNode = first;

	while(tmpNode!=NULL) {
		if(tmpNode->entryName!=NULL&&!strcmp(name,tmpNode->entryName)) return tmpNode->entryData;
		tmpNode = tmpNode->link;
	}
	return NULL;
/*
	while(1) {
		if( !strcmp( name, tmpNode->entryName ) )
			return tmpNode->entryData;
		tmpNode = tmpNode->link;
	}
	return NULL;
*/
}

// get entry file name by entry name
char * CCGI::getEntryFilenameByEntryName(const char * name)
{
	entryNode * tmpNode = first;

	while(tmpNode!=NULL) {
                if(!strcmp(name,tmpNode->entryName)) {
					return tmpNode->entryFilename;
				}
                tmpNode = tmpNode->link;
        }
        return NULL;

/*
	while(1) {
		if( !strcmp( name, tmpNode->entryName ) )
			return tmpNode->entryFilename;
		tmpNode = tmpNode->link;
	}
	return NULL;
*/
}

// get entry file size by entry name
long long int CCGI::getEntryFilesizeByEntryName(const char * name)
{
	entryNode * tmpNode = first;

	while(tmpNode!=NULL) {
                if(!strcmp(name,tmpNode->entryName)) return tmpNode->entryFilesize;
                tmpNode = tmpNode->link;
        }
        return -1;

/*
	while(1) {
		if( !strcmp( name, tmpNode->entryName ) )
			return tmpNode->entryFilesize;
		tmpNode = tmpNode->link;
	}
	return -1;
*/
}

// save to file by entry index
bool CCGI::writeToFileFromEntryData(const int src,const char * dst)
{
	long long int i = 0;
	long long int size = getEntryFilesize(src);
	ofstream fout(dst,ios::out);
	if( !fout ) return false;
	char * data = getEntryData(src);
	while( i != size ) {
		fout.put(*(data+i));
		i++;
	}
	fout.close();
	return true;
}

// save to file by entry name
bool CCGI::writeToFileFromEntryData(const char * src,const char * dst)
{
	long long int i = 0;
	long long int size = getEntryFilesizeByEntryName(src);
	ofstream fout(dst,ios::out);
	if( !fout ) return false;
	char * data = getEntryDataByEntryName(src);
	while( i != size ) {
		fout.put(*(data+i));
		i++;
	}
	fout.close();
	return true;
}
